#pragma once

#include <iostream>

using namespace std;

namespace ljh
{
	template<class T>
	class auto_ptr
	{
	public:
		
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{

		}

		~auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
				_ptr = nullptr;
			}
		}

		auto_ptr(const auto_ptr<T>& ap)
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr;
		}

		auto_ptr& operator=(const auto_ptr<T>& ap)
		{
			if (this != &ap)
			{
				if (_ptr)
				{
					delete _ptr;
				}

				_ptr = ap._ptr;
				ap._ptr = nullptr;

				return *this;
			}
		}

		T* operator->()
		{
			return _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T& operator[](size_t i)
		{
			return _ptr[i];
		}

	private:
		T* _ptr;
	};

	template<class T>
	class unique_ptr
	{
	public:
		explicit unique_ptr(T* ptr)
			:_ptr(ptr)
		{

		}

		~unique_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
				_ptr = nullptr;
			}
		}

		unique_ptr(const unique_ptr<T>& up) = delete;
		unique_ptr& operator=(const unique_ptr<T>& up) = delete;

		unique_ptr(unique_ptr<T>&& up)
			:_ptr(up._ptr)
		{
			up._ptr = nullptr;
		}

		unique_ptr& operator=(unique_ptr<T>&& up)
		{
			delete _ptr;
			_ptr = up._ptr;
			up._ptr = nullptr;

			return *this;
		}

		T* operator->()
		{
			return _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T& operator[](size_t i)
		{
			return _ptr[i];
		}

	private:
		T* _ptr;
	};

	template<class T>
	class shared_ptr
	{

	public:
		explicit shared_ptr(T* ptr=nullptr)
			:_ptr(ptr)
			,_pcount(new int(1))
		{

		}

		~shared_ptr()
		{
			if (--(*_pcount) == 0)
			{
				delete _ptr;
				delete _pcount;
				_ptr = nullptr;
				_pcount = nullptr;
			}
		}

		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pcount(sp._pcount)
		{
			(*_pcount)++;
		}
		
		//sp1=sp2
		shared_ptr& operator=(const shared_ptr<T>& sp)
		{
			if (_ptr != sp._ptr)
			{
				if (--(*_pcount) == 0)
				{
					delete _ptr;
					delete _pcount;
				}

				_ptr = sp._ptr;
				_pcount = sp._pcount;
				(*_pcount)++;
			}

			return *this;
		}

		T* operator->()
		{
			return _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T& operator[](size_t i)
		{
			return _ptr[i];
		}

		int use_count()const
		{
			return *_pcount;
		}

		T* get()const
		{
			return _ptr;
		}


	private:
		T* _ptr;
		int* _pcount;
	};

	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr() = default;

		~weak_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}

		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.get())
		{
			
		}

		weak_ptr& operator=(const shared_ptr<T>& sp)
		{
			_ptr = sp.get();

			return *this;
		}
	private:
		T* _ptr=nullptr;
	};

}